`timescale 1 ns / 1 ps

module sumador(
	input [15:0]a, b,
	input s_rb,	  	
	output [15:0]c
	);
	wire [15:0] b2;
	assign b2 = s_rb ?(~b + 1): b; 
	assign c = a + b2;
endmodule	  

module ialu(
	input [15:0]a, b,
	input [2:0]opcode,
	output reg[15:0] resultat
	// bit nad, bit or, bit xor, bit not, add, sub, sha, shl
   );
   wire[15:0] sum;
   wire[4:0] bneg;
   assign bneg =  ~b[4:0] + 1;
   sumador ia(a, b, opcode[0], sum);
	always@( * )
    case(opcode)
      3'b000: resultat = a & b;
      3'b001: resultat = a | b;	
      3'b010: resultat = a ^ b;
      3'b011: resultat = ~a;			   
      3'b100: resultat = sum;
      3'b101: resultat = sum;
      3'b110: resultat = (!b[4]) ? a <<< b[4:0] : a >>> bneg; // shift aritmetic
      3'b111: resultat = (!b[4]) ? a << b[4:0] : a >> bneg; // shift logic 
    endcase	  
endmodule


module comparador(
	input [15:0]a, b,
	input [2:0]opcode,
	output reg  resultat
	// bit nad, bit or, bit xor, bit not, add, sub, sha, shl
   );
   wire [15:0]sum;
   sumador ia(a, b, opcode[0], sum);
	always@( * )
    case(opcode)
      3'b000: resultat = (a < b);
      3'b001: resultat = (a <= b);	  
      3'b011: resultat = (a == b);			   
      3'b100: resultat = (a[15] > b[15]  || (a[15] == b[15] && a[14:0] < b[14:0]));		 	  
      3'b101: resultat = (a[15] > b[15]  || (a[15] == b[15] && a[14:0] <= b[14:0]));	
	  default resultat = 'bx;
    endcase	   
endmodule

module comp_float(
	input[31:0] a,b,
	input[2:0] opcode,
	output reg resultat
	);
	always @(*)
		case(opcode)	  //menorq menoroigual igual
		3'b000: resultat = (a[31]>b[31] && a[30:0]!=30'b0 && b[30:0]!=30'b0) || (a[31]==b[31] && a[30:0] < b[30:0]);
		3'b001: resultat = (a[31]>b[31])||  (a[30:0]==30'b0 && b[30:0]==30'b0) || (a[31]==b[31] && a[30:0] <= b[30:0]);
		default: resultat = (a[31>0]==b[31:0] || (a[30>0]==30'b0 ^^ b[30:0]==30'b0));
		endcase
		
endmodule 	 

module divisor_float(
	input [31:0]a, b,	
	input enable,
	input stop,	
	input clk,
	output[31:0] resultat,
	output enable_salida,
	output genera_excepcio	
	);
	reg enab, enac, enad,enae, enaf, enag;
    reg[31:0] ab, ac, ad, ae, af, ag, zerob, zeroc, zerod, zeroe, zerof, zerog;
    wire[31:0] aa;
    wire enaa, zeroa;
	assign enable_salida = enag;
	assign resultat = ag;			
	assign genera_excepcio = zerog;
	assign zeroa = (b[30:0] == 30'b0);
	always @ (posedge clk) if(!stop) begin	 
	  enab <= enaa;
	  enac <= enab;
	  enad <= enac;
	  enae <= enad;
	  enaf <= enae;
	  enag <= enaf;
	  ab <= aa;
	  ac <= ab;
	  ad <= ac;
	  ae <= ad;
	  af <= ae;
	  ag <= af;
	  zerob <= zeroa;
	  zeroc <= zerob;
	  zerod <= zeroc;
	  zeroe <= zerod;
	  zerof <= zeroe;
	  zerog <= zerof;
    end		
		
endmodule

module float_op(
	input [31:0]a, b,						
	input opcode,
	input enable,
	input stop,	
	input clk,
	output reg [31:0] resultat,
	output enable_salida,
	);										  
	wire mult, es_mult, zeroa;
	wire[31:0] result_mult, result_add, result_sub;
	assign mult = opcode == 3'b010;			
	multiplicador_float mf(.a(a), .b(b), .enable(mult), .stop(stop), .clk(clk), .resultat(result_mult), .enable_salida(es_mult));
	always @ (*) 
		if(es_mult) begin
			resultat = result_mult;
		end
		else if(enable && opcode == 3'b000) begin
			resultat = result_add;
		end
		else if(enable && opcode == 3'b001) begin
			resultat = result_sub;
		end

	
endmodule


module multiplicador_float(
	input [31:0]a, b,						
	input enable,
	input stop,
	input clk,
	output[31:0]  resultat,
	output enable_salida,
	// bit nad, bit or, bit xor, bit not, add, sub, sha, shl
   );
   assign enable_salida = enae;
   assign resultat = ae;
   reg enab, enac, enad,enae;
   reg[31:0] ab, ac, ad, ae;
   wire[31:0] aa;
   wire enaa;
   assign enaa = enable;
   assign aa = a;
   always @ (posedge clk) if(!stop) begin	   
	enab <= enaa;
	enac <= enab;
	enad <= enac;
	enae <= enad;
	ab <= aa;
	ac <= ab;
	ad <= ac;
	ae <= ad;
   end	   
endmodule	 

   

module multiplicador_enter(
	input [15:0]a, b,
	input [1:0]opcode, //MUL MULH MULHU
	input enable,
	input stop,
	input clk,
	output reg[15:0]  resultat,
	output enable_salida,
	// bit nad, bit or, bit xor, bit not, add, sub, sha, shl
   );		   			   			
   reg[31:0] op1a, op2a, op1b, op2b, op1c, op2c;
   reg[1:0] opcb, opcc, opcd, opce; 
   wire[1:0] opca;
   reg enb, enc, enad, ene;	  
   wire ena;
   wire[31:0] resa, resb, resc;	
   
   assign resa = op1a & {16{op2a[0]}} +  op1a<<1 & {16{op2a[1]}} + op1a<<2 & {16{op2a[2]}} +  op1a<<3 & {16{op2a[3]}} + 
   op1a<<4 & {16{op2a[4]}} +  op1a<<5 & {16{op2a[5]}} + op1a<<6 & {16{op2a[6]}} +  op1a<<7 & {16{op2a[7]}} + 
   op1a<<8 & {16{op2a[8]}} +  op1a<<9 & {16{op2a[9]}} + op1a<<10 & {16{op2a[10]}} +  op1a<<11 & {16{op2a[11]}};
   assign resb = resa+  op1b & {16{op2b[0]}} +  op1b<<1 & {16{op2b[1]}} + op1b<<2 & {16{op2b[2]}} +  op1b<<3 & {16{op2b[3]}} + 
   op1b<<4 & {16{op2b[4]}} +  op1b<<5 & {16{op2b[5]}} + op1b<<6 & {16{op2b[6]}} +  op1b<<7 & {16{op2b[7]}} + 
   op1b<<8 & {16{op2b[8]}} +  op1b<<9 & {16{op2b[9]}} + op1b<<10 & {16{op2b[10]}} +  op1b<<11 & {16{op2b[11]}};
   assign resc = resb +  op1c & {16{op2c[0]}} +  op1c<<1 & {16{op2c[1]}} + op1c<<2 & {16{op2c[2]}} +  op1c<<3 & {16{op2c[3]}} + 
   op1c<<4 & {16{op2c[4]}} +  op1c<<5 & {16{op2c[5]}} + op1c<<6 & {16{op2c[6]}} +  op1c<<7 & {16{op2c[7]}};	
   assign ena = enable;
   assign opca = opcode;
   assign enable_salida = enc;	 
   
   always @(*) begin
	if(opcode == 2'b01) begin
		op1a <= {{16{a[15]}},a};
		op2a <= {{16{b[15]}},b};
	end
	else begin
		op1a <= {{16'b0},a};
		op2a <= {{16'b0},b}; 
	end 
	case(opce) 
		2'b00: resultat = resc[15:0];
		default: resultat = resc[31:16];
	endcase	 
   end
   
   always @ (posedge clk) if(!stop) begin	
	op1b <= op1a << 12;
	op2b <= op2a >> 12;	  
	op1c <= op1b << 12;
	op2c <= op2b >> 12;	   
	enb <= ena;
	enc <= enb;	  	 
	opcb <= opca;
	opcc <= opcb; 
   end	 
   
   
endmodule


module corrector_salts(
	input [15:0] pcDecidit, pcnext,
	input haviemdesaltar,
	input saltat,
	output error, salta,
	output [15:0]pcCorrecte
	);
	assign salta = haviemdesaltar;
	assign error = (pcnext != pcDecidit);
	assign pcCorrecte = pcDecidit;
endmodule

module direccSalt(
	input [15:0] pcOrigen,pcmas, inmediatBranch, pcSaltAbs,
	
	input [15:0] opA, opB,
	input [2:0]salt_opcode,
	input eraSalt,
	input jmpType,
	output reg [15:0]pcDecidit,
	output reg salta
	);
	wire igual;
	assign igual = (salt_opcode == 3'b000);
	always@( * ) 
	if(!eraSalt) begin
		salta = 1'b0;
		
		pcDecidit = pcmas;
	end
	else if(jmpType) begin
		case(salt_opcode) 
      			3'b000: salta = (opA==opB) ? 1'b1 : 1'b0;
	      		3'b001: salta = (opA!=opB) ? 1'b1 : 1'b0;	  
	      		default: salta = 1'b1;	
		endcase
		pcDecidit = salta ? pcSaltAbs : pcmas;
	end
	else begin
		salta = igual ? (opA==opB):!(opA==opB); 
    		pcDecidit = salta ? pcOrigen + inmediatBranch : pcmas;	

	end
endmodule

module direccio_cache(
	input[15:0] direccio_base, //La que ens pasen en un registre
	input[15:0] inmediat,
	input[2:0] tamany,
	output[15:0] direccio
	);
	wire[15:0] in;
	assign in = {10'd0,inmediat};
	assign direccio = direccio_base + in;
	/*	always@( * )
    case(tamany)
      2'b00: direccio = (direccio_base + in);
      2'b01: direccio = (direccio_base + in<<1);	  
      2'b11: direccio = (direccio_base + in<<2);			   
      default direccio = (direccio_base + in<<1);
    endcase*/	
endmodule


module alu(	  
  //FLOAT
  input[31:0] DeAl_floatA, DeAl_floatB,	
  //ENTER
  input[15:0] DeAl_intA, DeAl_intB, DeAl_intC, DeAl_pcALU,	   
  //PEL LOAD STORE
  input[2:0] DeAl_memsize,  
  input[15:0] DeAl_pcmas, DeAl_pcnext,
  //El pc de la instruccio i el objectiu, que es el seguent, per evaluar la correctessa del salt
  
  input[2:0] DeAl_opcode,
  input DeAl_eALU, DeAl_eMUL, DeAl_eDIV, 
  input DeAl_eFLT, DeAl_eMEM, DeAl_eJMP,
  input DeAl_eIO, DeAl_eWRB, DeAl_eMOV,
  input DeAl_FRB, DeAl_wMEM, DeAl_wIO, DeAl_JMPType, DeAl_CMP,	  
  input DeAl_wTLB, DeAl_SRB, DeAl_eFLUSH, DeAl_eIID, DeAl_eRETI,
  input DeAl_eHALT,
  input DeAl_taken,
  //Els 4 primers bits del asm son 6543 els bits de operacio son els 210
  input clk, reset, stopALU,
  //El clk realment no caldria aqui sino al linkar moduls no?
  
  input[15:0] DeAl_ve,
  //
  output reg[31:0] AlCa_resultFloat, AlWb_data,
  output reg[15:0] AlCa_resultInt, AlCa_addrOut,
  output[15:0] AlCa_pc,
  output[15:0] AlFe_pctarget, AlFe_pcorigen,
  output  AlFe_ErrorSalt, AlFe_Salta,
  output reg [15:0] AlCa_ve, AlWb_exception,
  output[2:0] AlCa_tamany,
  output reg AlCa_enable, AlWb_valid,	 
  output AlCa_esLoad,	 
  );		
  
  
  
  wire result_Comp; 					
  wire [15:0] result_iALU;				
  wire [15:0] direccio_ldst;
  wire errorSalt;
  wire haviaDeSaltar;
  wire[15:0] pcCorrect;	   
  wire[15:0] pcSalt;
  reg [15:0] direccio_salt;
  wire[15:0] resultMult;
  wire acabaMUL, resultCompF, acaba_float, zerodiv, acaba_divisio;
  wire[15:0] pcCorrecte; 
  wire[31:0] resultat_float, resultat_divisio;
  wire hds;
  ialu ia(.a(DeAl_intA), .b(DeAl_intB),.opcode(DeAl_opcode[2:0]), .resultat(result_iALU));
  comparador comp(.a(DeAl_intA), .b(DeAl_intB), .opcode(DeAl_opcode[2:0]), .resultat(result_Comp));
  direccio_cache dc(.direccio_base(DeAl_intA), .inmediat(DeAl_intB), .tamany(DeAl_memsize), .direccio(direccio_ldst));
  direccSalt direcSalt(.pcOrigen(DeAl_pcALU), .pcmas(DeAl_pcmas), .inmediatBranch(DeAl_intB), .pcSaltAbs(DeAl_intA), .opA(DeAl_intC), .opB(16'b0000000000000000), .salt_opcode(DeAl_opcode), .eraSalt(DeAl_eJMP), .jmpType(DeAl_JMPType), .pcDecidit(pcCorrecte), .salta(hds)  );
  corrector_salts corrSalt(.pcDecidit(pcCorrecte),.pcnext(DeAl_pcnext),  .haviemdesaltar(hds), .saltat(DeAl_taken), .salta(AlFe_Salta), .error(AlFe_ErrorSalt), .pcCorrecte(AlFe_pctarget));
  multiplicador_enter muli(.a(DeAl_intA), .b(DeAl_intB), .opcode(DeAl_opcode[1:0]), .enable(DeAl_eMUL), .clk(clk),.stop(stopALU), .resultat(resultMult), .enable_salida(acabaMUL));	  
  comp_float compf(.a(DeAl_floatA), .b(DeAl_floatB), .opcode(DeAl_opcode), .resultat(resultCompF));
  float_op flop(.a(DeAl_floatA), .b(DeAl_floatB), .opcode(DeAl_opcode), .enable(DeAl_eFLT && DeAl_FRB), .stop(stopALU), .clk(clk), .resultat(resultat_float), .enable_salida(acaba_float));
  divisor_float divflo(.a(DeAl_floatA), .b(DeAl_floatB), .enable(DeAl_eDIV), .stop(stopALU), .clk(clk), .resultat(resultat_divisio), .enable_salida(acaba_divisio), .genera_excepcio(zerodiv));
		
  //Assignacions estatiques					   
  assign AlFe_pcorigen = DeAl_pcALU;	  
  assign AlCa_pc = DeAl_pcALU;
  assign AlCa_tamany = DeAl_memsize;
  assign AlCa_esLoad = !DeAl_wMEM;	  
  /*	   Assignacions "dinamiques"					  
  AlCa_addrOut = 16d'0;
  AlCa_resultInt = 16d'0;  
  AlCa_resultFloat = 32'd0;
  AlWb_data = 32'd0;     Si es un int, ignorar els primers 16 bits			
  AlCa_enable = 1'b0;
  AlWb_valid = 1'b0;	 
  Al_Ca_ve = DeAl_ve;
  AlWb_exception = DeAl_ve;
 */    
 //Instruccions que es comprotem com altres:
 // EI DI GETIID RETI RDS WRS
 //Falta saber que fer amb les de escriure pagina de tlb
 always@( * )	
	if(DeAl_eALU && !DeAl_CMP) begin
	  //Operacio ALU  
	    AlCa_addrOut = 16'b0;
        AlCa_resultInt = 16'b0;  
        AlCa_resultFloat = 32'd0;  
	  AlWb_data = {16'b0,result_iALU};	
	    AlCa_enable = 1'b0;	
      AlWb_valid = 1'b1;			      
	    AlCa_ve = DeAl_ve; 
      AlWb_exception = DeAl_ve;
	end	  
	else if(DeAl_eALU && DeAl_CMP) begin
	  //Operacio cmp
	    AlCa_addrOut = 16'b0;
        AlCa_resultInt = 16'b0;  
        AlCa_resultFloat = 32'd0;  
	  AlWb_data = {32{result_Comp}};	
	    AlCa_enable = 1'b0;	
      AlWb_valid = 1'b1;			      
	    AlCa_ve = DeAl_ve; 
      AlWb_exception = DeAl_ve;	 		
	end
    else if(DeAl_eFLT && DeAl_CMP) begin
	  //Operacio cmp float
	    AlCa_addrOut = 16'b0;
        AlCa_resultInt = 16'b0;  
        AlCa_resultFloat = 32'd0;  
	  AlWb_data = {32{resultCompF}};	
	    AlCa_enable = 1'b0;	
      AlWb_valid = 1'b1;			      
	    AlCa_ve = DeAl_ve; 
      AlWb_exception = DeAl_ve;	 		
	end
	
    else if(DeAl_eMOV || DeAl_eIO) begin 
	  //Operacio mov 
	  
	    AlCa_addrOut = 16'b0;
        AlCa_resultInt = 16'b0;  
        AlCa_resultFloat = 32'd0;  
	  AlWb_data = {16'b0,DeAl_intB};	
	    AlCa_enable = 1'b0;	
      AlWb_valid = 1'b1;			      
	    AlCa_ve = DeAl_ve; 
      AlWb_exception = DeAl_ve;
    end
    else if(DeAl_eMEM) begin
	  //Load Store 
	  AlCa_addrOut = direccio_ldst;
      AlCa_resultInt = DeAl_intC;  
      AlCa_resultFloat = DeAl_floatB;  
	    AlWb_data = {16'b0,DeAl_intB};	
	  AlCa_enable = 1'b1;	
        AlWb_valid = 1'b0;			      
	  AlCa_ve = DeAl_ve; 
        AlWb_exception = DeAl_ve;					
    end		   
  
else if(DeAl_eJMP && !DeAl_JMPType)begin
      //BZ BNZ
	    AlCa_addrOut = 16'b0;
            AlCa_resultInt = 16'b0;  
            AlCa_resultFloat = 32'b0;  
	    AlWb_data = 32'b0;	
	    AlCa_enable = 1'b0;	
            AlWb_valid = 1'b0;			      
	    AlCa_ve = DeAl_ve; 
            AlWb_exception = DeAl_ve;
	  
  end
  else if(DeAl_eJMP && DeAl_JMPType)begin
	  //  JZ JNZ JAL CALLS
	    AlCa_addrOut = 16'b0;
            AlCa_resultInt = 16'b0;  
            AlCa_resultFloat = 32'b0;  
	    AlWb_data = 32'b0;	
	    AlCa_enable = 1'b0;	
            AlWb_valid = 1'b0;			      
	    AlCa_ve = DeAl_ve; 
            AlWb_exception = DeAl_ve;
  end
  else if(acabaMUL) begin
	    AlCa_addrOut = 16'b0;
        AlCa_resultInt = 16'b0;  
        AlCa_resultFloat = 32'd0;  
	  AlWb_data = {16'b0,resultMult};	
	    AlCa_enable = 1'b0;	
      AlWb_valid = 1'b1;			      
	    AlCa_ve = DeAl_ve; 
      AlWb_exception = DeAl_ve; 
  end		   
  else if(DeAl_wTLB) begin
	  //Escritures a TLB, com coi sho envio a la gent?
	  AlCa_addrOut = 16'b0;
      AlCa_resultInt = 16'b0;  
      AlCa_resultFloat = 32'd0;  
	  AlWb_data = 32'b0;	
	  AlCa_enable = 1'b0;	
      AlWb_valid = 1'b0;			      
	  AlCa_ve = DeAl_ve; 
      AlWb_exception = DeAl_ve;
  end
  else if(acaba_float) begin
	    AlCa_addrOut = 16'b0;
        AlCa_resultInt = 16'b0;  
        AlCa_resultFloat = 32'd0;  
	  AlWb_data = resultat_float;	
	    AlCa_enable = 1'b0;	
      AlWb_valid = 1'b1;			      
	    AlCa_ve = DeAl_ve; 
      AlWb_exception = DeAl_ve;
  end
  else if(acaba_divisio) begin
  	    AlCa_addrOut = 16'b0;
        AlCa_resultInt = 16'b0;  
        AlCa_resultFloat = 32'd0;  
	  AlWb_data = resultat_divisio;	
	    AlCa_enable = 1'b0;	
      AlWb_valid = 1'b1;			      
	    AlCa_ve = DeAl_ve; 
      AlWb_exception = {DeAl_ve[15:4],zerodiv,DeAl_ve[2:0]};	
  end
  
  else if(DeAl_eFLUSH) begin
	  //Aixo diria que he de fer alguna comprovacio i si es certa dirli a WB, pero no estic segur. 
  end
  

  else if(DeAl_wIO) begin
	  //Escritures a TLB, com coi sho envio a la gent?
	  AlCa_addrOut = 16'b0;
          AlCa_resultInt = 16'b0;  
          AlCa_resultFloat = 32'd0;  
      AlWb_data = {16'b0,DeAl_intC};	
	  AlCa_enable = 1'b0;	
      AlWb_valid = 1'b1;			      
	  AlCa_ve = DeAl_ve; 
      AlWb_exception = DeAl_ve;
  end
    else if(DeAl_eRETI || DeAl_eIID || DeAl_eHALT)begin
	 
	    AlCa_addrOut = 16'b0;
            AlCa_resultInt = 16'b0;  
            AlCa_resultFloat = 32'b0;  
	    AlWb_data = 32'b0;	
	    AlCa_enable = 1'b0;	
            AlWb_valid = 1'b0;			      
	    AlCa_ve = DeAl_ve; 
            AlWb_exception = DeAl_ve;
  end
  else if(DeAl_wTLB)begin
	 
	    AlCa_addrOut = 16'b0;
            AlCa_resultInt = 16'b0;  
            AlCa_resultFloat = 32'b0;  
	    AlWb_data = {DeAl_intA, DeAl_intC};	
	    AlCa_enable = 1'b0;	
            AlWb_valid = 1'b1;			      
	    AlCa_ve = DeAl_ve; 
            AlWb_exception = DeAl_ve;
  end
  else begin
	  //Almenys nop y halt es comporten aixi q jo sapiga
  	    AlCa_addrOut = 16'b0;
        AlCa_resultInt = 16'b0;  
        AlCa_resultFloat = 32'd0;  
	    AlWb_data = 32'b0;	
	    AlCa_enable = 1'b0;	
        AlWb_valid = 1'b0;			      
	  AlCa_ve = DeAl_ve; 
      AlWb_exception = DeAl_ve;
  end  
endmodule	 